package main

import (
	"archive/zip"
	"bufio"
	"bytes"
	"crypto/md5"
	"errors"
	"fmt"
	"image"
	"image/png"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/fullsailor/pkcs7"
	"github.com/shogo82148/androidbinary/apk"

	"github.com/DHowett/go-plist"
)

const (
	FLAG_COMMENT = '#'
	FLAG_FIELD   = '$'
	SPLIT_CHAR   = '\t'
	SPLIT_TOKEN  = '='
)

var (
	ErrFieldsEmpty = errors.New("Err FieldsEmpty")
)

type FileTableParse struct {
	fields []string
	lines  [][]string
}

func (parse *FileTableParse) Fields() []string {
	return parse.fields
}

func (parse *FileTableParse) line(i int) []string {
	if len(parse.lines) <= i {
		return nil
	}
	return parse.lines[i]
}

func (parse *FileTableParse) fieldIdx(field string) int {
	for i, k := range parse.fields {
		if k == field {
			return i
		}
	}
	return -1
}

func (parse *FileTableParse) getItem(i int, field string) (bool, string) {
	idx := parse.fieldIdx(field)
	if idx < 0 {
		return false, ""
	}
	fields := parse.line(i)
	if fields != nil && len(fields) > idx {
		return true, fields[idx]
	}
	return false, ""
}

func (parse *FileTableParse) GetItemFloat32(i int, field string) float32 {
	if ok, item := parse.getItem(i, field); ok {
		if value, err := strconv.ParseFloat(item, 32); err == nil {
			return float32(value)
		}
	}
	return 0
}

func (parse *FileTableParse) GetItemFloat64(i int, field string) float64 {
	if ok, item := parse.getItem(i, field); ok {
		if value, err := strconv.ParseFloat(item, 32); err == nil {
			return value
		}
	}
	return 0
}

func (parse *FileTableParse) GetItemInt64(i int, field string) int64 {
	if ok, item := parse.getItem(i, field); ok {
		if value, err := strconv.ParseInt(item, 10, 64); err == nil {
			return value
		}
	}
	return 0
}

func (parse *FileTableParse) GetItemInt32(i int, field string) int32 {
	if ok, item := parse.getItem(i, field); ok {
		if value, err := strconv.ParseInt(item, 10, 32); err == nil {
			return int32(value)
		}
	}
	return 0
}

func (parse *FileTableParse) GetItemUint64(i int, field string) uint64 {
	if ok, item := parse.getItem(i, field); ok {
		if value, err := strconv.ParseUint(item, 10, 64); err == nil {
			return value
		}
	}
	return 0
}

func (parse *FileTableParse) GetItemUint32(i int, field string) uint32 {
	if ok, item := parse.getItem(i, field); ok {
		if value, err := strconv.ParseUint(item, 10, 32); err == nil {
			return uint32(value)
		}
	}
	return 0
}

func (parse *FileTableParse) GetItem(i int, field string) string {
	_, value := parse.getItem(i, field)
	return value
}

func (parse *FileTableParse) Count() int {
	return len(parse.lines)
}

func (parse *FileTableParse) Parse(rd io.Reader) error {
	reader := bufio.NewReader(rd)
	var (
		count int
		blen  int
	)
	parse.fields = nil
	parse.lines = make([][]string, 0, 50)
	for {
		bytes, _, _ := reader.ReadLine()
		blen = len(bytes)
		if blen == 0 {
			break
		}
		count++
		if bytes[0] == byte(FLAG_COMMENT) {
			continue
		}
		if bytes[0] == byte(FLAG_FIELD) {
			parse.fields = strings.Split(string(bytes[1:]), string(SPLIT_CHAR))
			continue
		}
		values := strings.Split(string(bytes), string(SPLIT_CHAR))
		parse.lines = append(parse.lines, values)
	}
	if len(parse.fields) == 0 {
		return ErrFieldsEmpty
	}
	return nil
}

func saveImg(img image.Image, fname string) {
	file, _ := os.Create(fname)
	defer file.Close()
	png.Encode(file, img)
}
func printFileInfo(fileName string) {
	fmt.Println("--------------文件信息--------------")
	fmt.Printf("文件路径:%s\n", fileName)
	f, err := os.Open(fileName)
	if err != nil {
		return
	}
	defer f.Close()
	md5hash := md5.New()
	if _, err := io.Copy(md5hash, f); err == nil {
		fmt.Printf("文件MD5:%x\n", md5hash.Sum(nil))
	}
}
func printVersionInfo(versionUrl string) {
	if len(versionUrl) > 0 {
		fmt.Println("--------------游戏version内容--------------")
		fmt.Printf("VersionUrl:%s\n", versionUrl)
		if url, uerr := url.Parse(versionUrl); uerr == nil {
			if hostList, herr := net.LookupHost(url.Host); herr == nil {
				fmt.Printf("%s解析ip=>%s\n", url.Host, hostList)
			}
			resp, err := http.Get(versionUrl)
			if err == nil {
				defer resp.Body.Close()
				ftp := new(FileTableParse)
				if err = ftp.Parse(resp.Body); err == nil {
					for _, v := range ftp.Fields() {
						fmt.Printf("%s:%s\n", v, ftp.GetItem(0, v))
					}
				} else {
					fmt.Println("未查询到Version数据")
				}
			} else {
				fmt.Println(err)
			}
		}
	}
}

func parseApk(fileName string) {
	fmt.Println("--------------包信息--------------")
	pkg, _ := apk.OpenFile(fileName)
	defer pkg.Close()
	manifest := pkg.Manifest()
	file := pkg.FindZipSuffix("META-INF/", ".RSA")
	if data, err := pkg.FindFile(file); err == nil {
		p7, _ := pkcs7.Parse(data)
		fmt.Printf("证书MD5:%x\n", md5.Sum(p7.Certificates[0].Raw))
	}

	fmt.Printf("包名:%s\n", manifest.Package)
	nameCh, _ := pkg.Label(nil)
	fmt.Printf("中文包名:%s\n", nameCh)

	fmt.Printf("版本号:%d\n", manifest.VersionCode)
	fmt.Printf("版本名:%s\n", manifest.VersionName)

	fmt.Printf("最小SDk:%d\n", manifest.SDK.Min)
	fmt.Printf("最大SDk:%d\n", manifest.SDK.Max)
	fmt.Printf("目标SDk:%d\n", manifest.SDK.Target)
	fmt.Println("--------------页面列表--------------")

	for _, act := range manifest.App.Activities {
		fmt.Printf("%s\n", act.Name)
	}

	fmt.Println("--------------配置列表--------------")
	var versionUrl string
	for _, meta := range manifest.App.MetaDataes {
		fmt.Printf("%s:%s\n", meta.Name, meta.Value)
		if "D2_PlatformID" == meta.Name {
			if data, err := pkg.FindFile("assets/res/srvHost"); err == nil {
				versionUrl = fmt.Sprintf("%sad/version_%s.txt", string(data), meta.Value)
			}
		}
	}
	var defaultImg = func() {
		if img, err := pkg.Icon(nil); err == nil {
			saveImg(img, nameCh+".png")
		}
	}
	if data, err := pkg.FindFile("res/drawable-hdpi-v4/icon.png"); err == nil {
		if img, err1 := png.Decode(bytes.NewReader(data)); err1 == nil {
			saveImg(img, nameCh+".png")
		} else {
			defaultImg()
		}
	} else {
		defaultImg()
	}

	printVersionInfo(versionUrl)
}

func parseIpaChildren(k string, v interface{}) {
	switch v.(type) {
	case map[string]interface{}:
		{
			for k1, v1 := range v.(map[string]interface{}) {
				parseIpaChildren(k1, v1)
			}
		}
	case string:
		fmt.Printf("%s:%s\n", k, v)
	}
}

func parseIpa(name string) {
	r, err := zip.OpenReader(name)
	if err != nil {
		return
	}
	defer r.Close()
	var (
		versionUrl string
		platformID string
		nameCh     string
		imgBuff    []byte
	)
	fmt.Println("--------------包信息--------------")
	var fileReader = func(file *zip.File) []byte {
		rc, err := file.Open()
		if err != nil {
			return nil
		}
		buf := make([]byte, file.FileInfo().Size())
		_, err = io.ReadFull(rc, buf)
		if err != nil {
			return nil
		}
		return buf
	}
	for _, file := range r.File {
		fileName := file.FileInfo().Name()
		if fileName == "Info.plist" {
			if buf := fileReader(file); buf != nil {
				var info_map map[string]interface{}
				_, err = plist.Unmarshal(buf, &info_map)
				if err != nil {
					return
				}
				for k, v := range info_map {
					switch k {
					case "D2_PlatformID":
						platformID = v.(string)
					case "CFBundleDisplayName":
						nameCh = v.(string)
						fmt.Printf("中文包名:%s\n", nameCh)
					default:
						parseIpaChildren(k, v)
					}

				}
			}
		} else if fileName == "srvHost" {
			if buf := fileReader(file); buf != nil {
				versionUrl = string(buf)
			}
		} else if fileName == "AppIcon40x40@2x.png" {
			imgBuff = fileReader(file)
		}
	}

	if imgBuff != nil && len(nameCh) > 0 {
		if img, err := png.Decode(bytes.NewReader(imgBuff)); err == nil {
			saveImg(img, nameCh+".png")
		}
	}

	if len(versionUrl) > 0 && len(platformID) > 0 {
		versionUrl = fmt.Sprintf("%sios/version_%s.txt", versionUrl, platformID)
		printVersionInfo(versionUrl)
	}
}

func main() {
	flieName := os.Args[1]
	printFileInfo(flieName)
	if strings.HasSuffix(flieName, ".apk") {
		parseApk(flieName)
	} else if strings.HasSuffix(flieName, ".ipa") {
		parseIpa(flieName)
	} else {
		fmt.Println("必须是ipa或者apk文件!!!")
	}
	bufio.NewReader(os.Stdin).ReadLine()
}
